#include "TriangleRender.h"
#include <memory>
namespace Sivin {
  TriangleRender::TriangleRender() {
    mShader = std::make_unique<Shader>("./../shader/triangle/vert.vs", "./../shader/triangle/frag.fs");

    /**
     * 这里我们定义了一些数据，这些数据本身并没有意义
     * 对于OpenGL而言，它并不知道这些数据是什么，因此需要我们需要告诉OpenGL
     * 因此我们需要在显卡中创建一个缓冲区来存放这部分数据，同时我们还需要
     * 告诉OpenGL，这块内存是用来存放顶点的，由于一个顶点可能有多种属性，如位置，颜色
     * 纹理，法线等，这些数据都可能被放到这块内存中，而顶点数据最终是要被输入到顶点着色器
     * 因此我们还需要对这块缓存数据进行解释，即告诉OpenGL该如何理解这部分数据，
     * 即：哪部分数据应该输入给顶点位置属性，哪部分数据应该输入给顶点颜色属性。
     */
    mVertices = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f, 0.5f, 0.0f};
  }

  TriangleRender::~TriangleRender() {
  }


  void TriangleRender::onGLInit() {
    mShader->init();
    /**
     * 顶点数组对象：负责保存和管理一系列的顶点数据
     * 这些顶点数据被存放到顶点缓冲对象中：VBO中
     * 返回n个可用的顶点数组对象的名称，这里并没有创建顶点数组对象
     */
    glGenVertexArrays(1, &mVAO);

    /**
     * 这个函数只是返回n个未被使用的缓存对象名
     * 在未调用glBindBuffer之前，没有任何缓存对象和这个名字关联
     */
    GLuint vbo;
    glGenBuffers(1, &vbo);

    /**
     * 绑定当前的顶点数组对象，如果指定名字的对象不存在，
     * 则会创建一个顶点数组对象并且绑定到当前上下文中
     * 接下来关于顶点相关的设置都将和当前绑定的顶点数组对象关联
     * 对顶点的操作本质上就是对顶点属性的操作
     * 其实就是记录下面的：
     *  glVertexAttribPointer 和 glEnableVertexAttribArray 上的一些操作
     */
    glBindVertexArray(mVAO);

    /**
     * 将一个缓冲区对象绑定到指定的目标上
     * 如果指定名字的buffer不存在，则会创建一个buffer，并且和指定的目标绑定
     * GL_ARRAY_BUFFER：表示该buffer是用于存放顶点属性
     * 绑定成功后，意味着接下来对顶点属性缓存对象的操作，都将和当前的vbo关联
     */
    glBindBuffer(GL_ARRAY_BUFFER, vbo);


    /**
     * 为当前绑定的目标关联缓存对象进行赋值：这里数据将会传递到vbo中
     * 1.操作的对象目标：表示为GL_ARRAY_BUFFER的目标buffer赋值
     * 2.数据的大小，单位字节
     * 3.数据的起始指针
     * 4.数据的使用方式：
     *  GL_STATIC_DRAW：数据不会或几乎不会改变。
     *  GL_DYNAMIC_DRAW：数据会动态变化
     *  GL_STREAM_DRAW ：数据每次绘制时都会改变。
     *  这里的更改是指，我们是否会频繁的修改vbo的数据，顶点着色器里修改顶点数据，并没有修改到vbo里的数据
     *  这里的参数，决定了显卡会如何存放这部分数据，如果GL_DYNAMIC_DRAW或GL_STREAM_DRAW，则显卡会将数据
     *  存放到高速写入的内存部分
     */
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * mVertices.size(), mVertices.data(), GL_STATIC_DRAW);


    /**
     * 这个函数是指定顶点着色器中顶点属性对应的数据指向，并且指定如何解释指向的数据
     * 这里指向的是当前绑定到GL_ARRAY_BUFFER目标上的顶点缓存对象
     * 
     * 顶点数据最终要输入到顶点着色器程序进行处理，顶点着色器允许我们指定任何以 '顶点属性' 为形式的输入。
     * 在OpenGL中，顶点可以通过多种属性来描述：
     *  1.位置属性：表示顶点在3D空间中的位置，通常用x、y和z三个分量来描述。
     *  2.法向量：表示与顶点相关的光照信息，用于计算顶点的表面光照效果
     *  3.颜色：表示顶点的颜色值，通常使用RGBA四个分量来描述。
     *  4.纹理坐标：表示顶点在纹理映射中的位置，通常用s和t两个分量来描述。
     * 这些顶点数据是以数组的形式统一存放到缓存里，所以在渲染前，我们必须告诉OpenGL该如何解释这些数据
     * 
     * 参数解释：
     * 1.对应vertexShader中：layout(location = 0)中location的值，表示将数据传递给这个顶点属性
     * 
     * 2.Shader程序中对应location的属性类型的分量个数，例如aPosition属性类型为vec3，
     *   这个属性由3个分量组成，这里必须和shader里对应起来，否则shader里的属性将无法读完整整个属性的值
     * 
     * 3.Shader程序中对应location的属性每个分量的类型大小：vec3有三个分量，每个分量是float类型：写GL_FLOAT
     * 
     * 4.是否需要数据被归一化处理：如果是GL_TRUE：需要，GL_FALSE：不需要
     *   对于颜色数据，我们可以使用整数(255,123,88,255)和浮点数(1.0f,0.3f,0.2f,1.0f)两种形式表示。
     *   如果颜色数据使用的是整数，shader需要的是浮点数，我们需要将123整数，换算成0~1.0之间=》123 * 1.0/255
     *   归一化：当前数据的值/当前整型的空间大小：8位是255
     *   
     *   对于浮点数，不需要归一化处理
     * 
     * 5.步长：表示当前location属性，两个顶点之间的数据间隔，这个顶点的位置属性读完后，应该移动多少字节大小
     *   能读取下一个顶点的位置属性的值：(3 * sizeof(float))
     * 
     * 6. 表示数据在缓冲中的起始偏移位置：这里我们是0
     */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);

    //顶点属性默认是关闭，我们需要开启顶点属性
    //参数：shader中的location
    glEnableVertexAttribArray(0);

    //通过上面的一系列操作，vbo已经成功和顶点属性绑定。接下来已经不需要对vbo进行操作了，将目标和VBO解绑
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    //如果后续不需要操作该VAO对象，可以解绑当前VAO,这样可以防止一些后续的操作修改当前的VAO
    glBindVertexArray(0);
  }


  void TriangleRender::onRender() {
    mShader->start();
    glBindVertexArray(mVAO);
    //绘制的图像是什么，从第几个顶点开始，绘制的顶点个数是多少
    glDrawArrays(GL_TRIANGLES, 0, 3);
    mShader->end();
  }

}// namespace Sivin